package com.yycx.module.marketing.provider.service.impl;

import com.yycx.common.constants.CommonConstants;
import com.yycx.common.mybatis.base.service.impl.BaseServiceImpl;
import com.yycx.common.base.entity.EntityMap;
import com.yycx.common.mybatis.callback.CriteriaQueryCallBack;
import com.yycx.common.mybatis.model.ResultBody;
import com.yycx.common.mybatis.query.CriteriaQuery;
import com.yycx.common.mybatis.query.CriteriaSave;
import com.yycx.common.mybatis.query.CriteriaUpdate;
import com.yycx.common.security.OpenHelper;
import com.yycx.common.utils.ApiAssert;
import com.yycx.common.utils.DateUtils;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.module.marketing.client.entity.MkgCoupon;
import com.yycx.module.marketing.client.entity.MkgCouponUser;
import com.yycx.module.marketing.provider.mapper.CouponUserMapper;
import com.yycx.module.marketing.provider.service.CouponService;
import com.yycx.module.marketing.provider.service.CouponUserService;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 实现类
 *
 * @author flyme
 * @date 2019-10-22
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class CouponUserServiceImpl extends BaseServiceImpl<CouponUserMapper, MkgCouponUser> implements CouponUserService {


    @Resource
    private CouponService couponService;

    @Override
    public ResultBody beforeAdd(CriteriaSave cs, MkgCouponUser mkgCouponUser, EntityMap extra) {
        Long userId = OpenHelper.getUserId();
        ApiAssert.isNotEmpty("参数不能为空", mkgCouponUser);
        Long couponId = mkgCouponUser.getCouponId();

        ApiAssert.isNotEmpty("couponId不能为空", couponId);
        RLock lock = redisLock.lock(couponId);
        try {
            MkgCoupon coupon = couponService.getById(couponId);
            ApiAssert.isNotEmpty("优惠券不存在", coupon);
            Integer state = coupon.getState();
            ApiAssert.eq("优惠券已下架", state, CommonConstants.ENABLED);

            //优惠券总量
            Integer provideNum = coupon.getProvideNum();

            ApiAssert.gtzero("优惠券已领取完毕", provideNum);

            Long num = countByCouponAndUserId(userId, couponId);
            Integer receiveNum = coupon.getReceiveNum();
            ApiAssert.isTrue("领取数量已达上限", receiveNum >= num);
            mkgCouponUser.setCouponId(couponId);
            mkgCouponUser.setUserId(userId);
            mkgCouponUser.setUserExpireBeginDate(coupon.getExpiryBeginTime());
            mkgCouponUser.setUserExpireEndDate(coupon.getExpiryEndTime());
            mkgCouponUser.setShopId(coupon.getShopId());
            mkgCouponUser.setUseState(CommonConstants.DISABLED);
        } finally {
            redisLock.unlock(lock);
        }

        return ResultBody.ok();
    }

    @Override
    public ResultBody afterAdd(CriteriaSave cs, MkgCouponUser mkgCouponUser, EntityMap extra) {
        couponService.updateSurplusNum(mkgCouponUser.getCouponId(), 1);
        return ResultBody.msg("领取成功");
    }

    @Override
    public ResultBody beforePageList(CriteriaQuery<MkgCouponUser> cq, MkgCouponUser mkgCouponUser, EntityMap requestMap) {
        cq.select(MkgCouponUser.class, "*");
        cq.createJoin(MkgCoupon.class);
        return ResultBody.ok();
    }


    @Override
    public ResultBody beforeGet(CriteriaQuery<MkgCouponUser> cq, MkgCouponUser mkgCouponUser, EntityMap requestMap) {
        Long userId = OpenHelper.getUserId();
        ApiAssert.isNotEmpty("请先登录", userId);
        Long couponId = requestMap.getLong("couponId");
        ApiAssert.isNotEmpty("优惠券Id不能为空", couponId);
        cq.select(MkgCoupon.class, "*");
        cq.eq(true, "couponId", couponId);
        cq.eq(true, "userId", userId);
        return ResultBody.ok();
    }


    @Override
    public EntityMap getCoupon(Long couponId) {
        CriteriaQuery cq = new CriteriaQuery(MkgCouponUser.class);
        Long userId = OpenHelper.getUserId();
        ApiAssert.isNotEmpty("请先登录", userId);
        ApiAssert.isNotEmpty("优惠券Id不能为空", couponId);
        cq.select(MkgCoupon.class, "*");
        cq.eq(MkgCoupon.class, "couponId", couponId);
        cq.eq(MkgCouponUser.class, "userId", userId);
        cq.createJoin(MkgCoupon.class);
        return findOne(cq);
    }

    @Override
    public EntityMap getCouponById(Long couponUserId, CriteriaQueryCallBack cqc) {
        CriteriaQuery cq = new CriteriaQuery(MkgCouponUser.class);
        if (FlymeUtils.isNotEmpty(cqc)) {
            cqc.init(cq);
        }
        cq.eq(MkgCouponUser.class, "couponUserId", couponUserId);
        cq.createJoin(MkgCoupon.class).setMainField("couponId").setJoinField("couponId");
        return findOne(cq);
    }


    /**
     * 查询个人未失效优惠券
     *
     * @param userId
     * @return
     */
    @Override
    public List<EntityMap> listByUserId(Long userId, Integer useState) {
        CriteriaQuery cq = new CriteriaQuery(MkgCouponUser.class);
        cq.eq("useState", useState);
        cq.eq(true, "userId", userId);
        cq.select(MkgCoupon.class, "*");
        cq.select(MkgCouponUser.class, "couponUserId");
        cq.createJoin(MkgCoupon.class);
        if (useState.equals(CommonConstants.DISABLED)) {
            //未使用的加上日期条件
            cq.ge("userExpireEndDate", DateUtils.formatDate());
            cq.le("userExpireBeginDate", DateUtils.formatDate());
        }
        return selectEntityMap(cq);
    }


    /**
     * 查询个人未失效优惠券数量
     *
     * @param userId
     * @return
     */
    @Override
    public long countByUserId(Long userId) {
        CriteriaQuery cq = new CriteriaQuery(MkgCouponUser.class);
        cq.eq("useState", CommonConstants.DISABLED);
        cq.eq(true, "userId", userId);
        //未使用的加上日期条件
        cq.ge("userExpireEndDate", DateUtils.formatDate());
        cq.le("userExpireBeginDate", DateUtils.formatDate());
        return count(cq);
    }

    /**
     * 查询购物车中优惠券列表
     *
     * @param userId
     * @param list
     * @return
     */
    @Override
    public EntityMap listByShopCard(Long userId, List<EntityMap> list) {
        EntityMap all = new EntityMap();
        List<EntityMap> result1 = new ArrayList<>();
        List<EntityMap> result2 = new ArrayList<>();
        //查询未使用优惠券
        List<EntityMap> couponList = listByUserId(userId, 0);
        for (EntityMap coupon : couponList) {
            //优惠券类型
            Integer couponType = coupon.getInt("couponType", 0);
            Long couponShopId = coupon.getLong("shopId");
            //满减条件金额
            BigDecimal withAmount = coupon.getBigDecimal("withAmount");
            for (EntityMap map : list) {
                BigDecimal shopTotalAmount = map.getBigDecimal("shopTotalAmount");
                Long shopId = map.getLong("shopId");
                if (couponShopId.equals(shopId)) {
                    //满减
                    if (couponType.equals(2)) {
                        if (FlymeUtils.ge(shopTotalAmount, withAmount)) {
                            result1.add(coupon);
                        } else {
                            result2.add(coupon);
                        }
                    }
                }
            }
        }
        all.add("mayYesList", result1);
        all.add("mayNoList", result2);
        all.add("mayNoCount", result2.size());
        all.add("mayYesCount", result1.size());
        return all;
    }

    @Override
    public EntityMap listByShopIdAndAmount(Long shopId, BigDecimal productAmount, Long userId) {
        EntityMap all = new EntityMap();
        List<EntityMap> result1 = new ArrayList<>();
        List<EntityMap> result2 = new ArrayList<>();
        //查询未使用优惠券
        List<EntityMap> couponList = listByUserId(userId, 0);
        for (EntityMap coupon : couponList) {
            //优惠券类型
            Integer couponType = coupon.getInt("couponType", 0);
            Long couponShopId = coupon.getLong("shopId");
            //满减条件金额
            BigDecimal withAmount = coupon.getBigDecimal("withAmount");
            if (couponShopId.equals(shopId)) {
                //满减
                if (couponType.equals(2)) {
                    if (FlymeUtils.ge(productAmount, withAmount)) {
                        result1.add(coupon);
                    } else {
                        result2.add(coupon);
                    }
                }
            }

        }
        all.add("mayYesList", result1);
        all.add("mayNoList", result2);
        all.add("mayNoCount", result2.size());
        all.add("mayYesCount", result1.size());
        return all;
    }


    /**
     * 计算优惠金额
     *
     * @param couponUserIds
     * @param shopId
     * @return
     */
    @Override
    public EntityMap calculateCouponAmount(String couponUserIds, Long shopId) {
        EntityMap result = new EntityMap();
        ApiAssert.isNotEmpty("优惠券ID不能为空", couponUserIds);
        CriteriaQuery cq = new CriteriaQuery(MkgCouponUser.class);
        String[] couponUserIdArray = couponUserIds.split(",");
        if (FlymeUtils.isNotEmpty(shopId)) {
            List<Long> shopCouponUserIds = selectCouponUserIds(couponUserIdArray, shopId);
            result.put("shopCouponUserIds", shopCouponUserIds);
            cq.eq(MkgCouponUser.class, "shopId", shopId);
        }
        cq.in(true, "couponUserId", couponUserIdArray);
        cq.eq("useState", CommonConstants.DISABLED);

        cq.select(MkgCoupon.class, "*");
        cq.createJoin(MkgCoupon.class);
        //未使用的加上日期条件
        cq.ge("userExpireEndDate", DateUtils.formatDate());
        cq.le("userExpireBeginDate", DateUtils.formatDate());
        //查询符合条件的优惠券
        List<EntityMap> list = selectEntityMap(cq);
        BigDecimal totalCouponAmount = new BigDecimal("0");
        for (EntityMap entityMap : list) {
            BigDecimal couponAmount = entityMap.getBigDecimal("couponAmount");
            totalCouponAmount = totalCouponAmount.add(couponAmount);
        }
        result.put("couponUserIds", couponUserIds);
        result.put("totalCouponAmount", totalCouponAmount);
        return result;
    }

    /**
     * 计算优惠金额
     *
     * @param couponUserIds
     * @return
     */
    @Override
    public EntityMap calculateCouponAmount(String couponUserIds) {
        return calculateCouponAmount(couponUserIds, null);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public ResultBody CouponcouPageList(Map params) {
        CriteriaQuery cq = new CriteriaQuery(params, MkgCouponUser.class);
        Long userId = OpenHelper.getUserId();
        cq.select(MkgCouponUser.class, "userId", "useState", "useTime");
        cq.select(MkgCoupon.class, "*");
        cq.eq(MkgCoupon.class, "state", CommonConstants.ENABLED);
        cq.eq(MkgCouponUser.class, "userId", userId);
        cq.createJoin(MkgCoupon.class);
        cq.groupBy("couponAmount");
        return basePageList(cq);
    }

    /**
     * 查询店铺优惠券领取数量
     *
     * @param userId
     * @param couponId
     * @return
     */
    @Override
    public Long countByCouponAndUserId(Long userId, Long couponId) {
        CriteriaQuery cq = new CriteriaQuery(MkgCouponUser.class);
        cq.eq(true, "userId", userId);
        cq.eq(true, "couponId", couponId);
        return count(cq);
    }

    @Override
    public List<Long> selectCouponUserIds(String[] couponUserIds, Long shopId) {
        CriteriaQuery cq = new CriteriaQuery(MkgCouponUser.class);
        cq.select(MkgCouponUser.class, "couponUserId");
        cq.eq(true, "shopId", shopId);
        cq.in(true, "couponUserId", couponUserIds);
        return listObjs(cq, e -> Long.parseLong(e.toString()));
    }

    @Override
    public Boolean updateCouponState(String couponUserIds, Integer useState) {
        if (FlymeUtils.isNotEmpty(couponUserIds)) {
            CriteriaUpdate cu = new CriteriaUpdate();
            cu.in(true, "couponUserId", couponUserIds.split(","));
            cu.set(true, "useState", useState);
            cu.set(true, "useTime", DateUtils.getDateTime());
            return update(cu);
        } else {
            return false;
        }
    }

    @Override
    public Boolean updateCouponState(Long orderId, String couponUserIds, Integer useState) {
        if (FlymeUtils.isNotEmpty(couponUserIds)) {
            CriteriaUpdate cu = new CriteriaUpdate();
            cu.in(true, "couponUserId", couponUserIds.split(","));
            cu.set(true, "useState", useState);
            cu.set(true, "useTime", DateUtils.getDateTime());
            cu.set(true, "orderId", orderId);
            return update(cu);
        } else {
            return false;
        }
    }

    /**
     * 统计优惠券可用数量
     *
     * @param userId
     * @param productId
     * @param amount
     * @return
     */
    @Override
    public Integer countEnabledCoupon(Long userId, Integer productId, BigDecimal amount) {
        CriteriaQuery<MkgCouponUser> cq = new CriteriaQuery(MkgCouponUser.class);
        cq.addSelect("COALESCE(COUNT(*),0) canUseNum");
        cq.eq(MkgCouponUser.class, "userId", userId);
        cq.le("userExpireBeginDate", DateUtils.formatDate());
        cq.ge("userExpireEndDate", DateUtils.formatDate());
        if (FlymeUtils.isNotEmpty(productId)) {
            cq.and(true, q -> {
                q.in("productId", 0, productId);
            });
        }
        if (FlymeUtils.isNotEmpty(amount) && FlymeUtils.gtzero(amount)) {
            //订单金额符合优惠券使用条件
            cq.le(MkgCoupon.class, "withAmount", amount);
        }
        cq.eq("useState", 0);
        cq.eq("isDelete", 0);
        cq.createJoin(MkgCoupon.class).setMainField("couponId").setJoinField("couponId");
        EntityMap one = findOne(cq);
        return one.getInt("canUseNum");
    }

    @Override
    public Integer countDisabledCoupon(Long userId, Integer productId, BigDecimal amount) {
        return countDisabledCoupon(userId, productId, amount, null);
    }

    /**
     * 统计不可用优惠券数量
     *
     * @param userId
     * @param productId
     * @param amount
     * @param callBack
     * @return
     */
    @Override
    public Integer countDisabledCoupon(Long userId, Integer productId, BigDecimal amount, CriteriaQueryCallBack callBack) {
        CriteriaQuery<MkgCouponUser> cq = new CriteriaQuery(MkgCouponUser.class);
        cq.addSelect("COALESCE(COUNT(*),0) notUseNum");
        String nowDate = DateUtils.formatDate();
        cq.eq(true, "userId", userId);
        cq.eq("useState", 0);
        cq.eq("isDelete", 0);
        cq.ge("userExpireEndDate", nowDate);
        if (FlymeUtils.isNotEmpty(callBack)) {
            callBack.init(cq);
        }
        //不可用的情况(时间未到,不符合产品类型，不符合使用金额)
        cq.and(e -> e.gt("userExpireBeginDate", nowDate).or().notIn(FlymeUtils.isNotEmpty(productId), "productId", productId, 0).or().gt(FlymeUtils.gtzero(amount), "withAmount", amount));

        cq.createJoin(MkgCoupon.class).setMainField("couponId").setJoinField("couponId");
        EntityMap one = findOne(cq);
        return one.getInt("notUseNum");
    }

    /**
     * 统计过期优惠券数量
     *
     * @param userId
     * @return
     */
    @Override
    public Integer countExpireCoupon(Long userId) {
        CriteriaQuery<MkgCouponUser> cq = new CriteriaQuery(MkgCouponUser.class);
        cq.addSelect("COALESCE(COUNT(*),0) notUseNum");
        String nowDate = DateUtils.formatDate();
        cq.eq(true, "userId", userId);
        cq.eq("useState", 0);
        cq.eq("isDelete", 0);
        cq.lt("userExpireEndDate", nowDate);
        EntityMap one = findOne(cq);
        return one.getInt("notUseNum");
    }

    //计算折扣金额
    @Override
    public BigDecimal calculateDiscountAmountByCouponUserId(Long couponUserId, BigDecimal orderAmount) {
        MkgCoupon coupon = null;
        if (FlymeUtils.isNotEmpty(couponUserId)) {
            MkgCouponUser couponUser = getById(couponUserId);
            ApiAssert.isNotEmpty("优惠券不存在", couponUser);
            coupon = couponService.getById(couponUser.getCouponId());
            ApiAssert.isNotEmpty("优惠券不存在", coupon);
        }
        BigDecimal discountAmount = new BigDecimal("0");
        if (FlymeUtils.isNotEmpty(coupon)) {
            //优惠形式 1折扣 2满减
            Integer couponType = coupon.getCouponType();
            BigDecimal couponAmount = FlymeUtils.getBigDecimal(coupon.getCouponAmount(), "0");
            //折扣券
            if (couponType == 1) {
                discountAmount = orderAmount.multiply(BigDecimal.ONE.subtract(couponAmount));
                BigDecimal limitAmount = FlymeUtils.getBigDecimal(coupon.getLimitAmount(), "0");
                //limitAmount>0进行限制
                if (FlymeUtils.gtzero(limitAmount)) {
                    //折扣金额大于优惠上限
                    if (FlymeUtils.gt(discountAmount, limitAmount)) {
                        discountAmount = limitAmount;
                    }
                }
            }
            //满减
            if (couponType == 2) {
                BigDecimal withAmount = FlymeUtils.getBigDecimal(coupon.getWithAmount(), "0");
                discountAmount = couponAmount;
                if (!withAmount.equals(new BigDecimal(-1))) {
                    if (FlymeUtils.lt(orderAmount, withAmount)) {
                        ApiAssert.failure("优惠券不符合使用条件");
                    }
                }
            }
        }
        return discountAmount;
    }

    @Override
    public Boolean deleteByState(Long couponUserId, Integer useState) {
        if (useState.equals(1)) {
            CriteriaUpdate cu = new CriteriaUpdate();
            cu.set("isDelete", 1);
            cu.eq(true, "couponUserId", couponUserId);
            return update(cu);
        } else {
            return removeById(couponUserId);
        }
    }
}
